package com.mon4cc.simulation;

import com.mon4cc.cmon.adaption.BPMNAdaptor;
import com.mon4cc.simulation.logWriter.LogWriter;
import com.mon4cc.simulation.utils.BpmnUtils;
import org.jbpt.pm.FlowNode;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.util.Assert;
import org.springframework.util.ObjectUtils;

import java.util.Set;

/**
 * @author Ping
 * @date 2022/12/19 11:32
 **/
public abstract class FlowNodeSimulatedInstance {
    protected LogWriter writer ;
    protected TokenBasedBPMNSimulation bpmnSimulation ;
    protected FlowNode flowNode ;
    protected boolean enabled ;
    protected String name ;


    public FlowNodeSimulatedInstance(TokenBasedBPMNSimulation bpmnSimulation, FlowNode flowNode) {
        this.bpmnSimulation = bpmnSimulation ;
        this.flowNode = flowNode;
        this.enabled = false ;
        this.name = flowNode.getName() ;
        this.writer = bpmnSimulation.getWriter() ;
    }

    public void doExecute(ExecuteContext context) {
        if (context.generatedEnoughEvents()) {
            System.out.println("----END ---") ;
            return ;
        }
        // check End Event was involved in enabledInstance list.
        if (context.endEventArrived()) {
            context.getSimulationInstance(bpmnSimulation.getEndEvent()).doExecute(context);
            return ;
        }
        doSimulate(context) ;
        // current instance was finished, then it enabled state was reset.
        this.emptyToken();
        FlowNodeSimulatedInstance nextInstance = this.nextInstance(context) ;
        Assert.notNull(nextInstance, "Next instance should not null, since current instance was not end instance.") ;
        nextInstance.doExecute(context);

    }
    protected void doSimulate(ExecuteContext context) {

    }

    /**
     * For normal task flow node, the process to get next instance,
     * @param context
     * @return
     */
    public FlowNodeSimulatedInstance nextInstance(ExecuteContext context) {
        Set<FlowNode> nextNodes = BpmnUtils.nextFlowNodes(this.bpmnSimulation.getBpmn(), this.flowNode) ;
        if (ObjectUtils.isEmpty(nextNodes)) {
            Assert.state(BPMNAdaptor.isEndEvent(this.flowNode),
                "The process might unsound, since the not end event flow node does not have next node ");
            return null ;
        }
        if (nextNodes.contains(this.bpmnSimulation.getEndEvent())) {
            return BpmnUtils.toInstance(this.bpmnSimulation.getEndEvent(), this.bpmnSimulation) ;
        }
        // add next nodes, as candidates,  to the context
        context.updateEnabledSimulatedInstance(this, nextNodes);
        return context.getNextEnabledInstance();
    }


    public boolean isEnabledNow() {
        return this.enabled ;
    }
    public void emptyToken() {
        this.enabled = false ;

    }

    public void getTokenFrom(FlowNodeSimulatedInstance fromNode) {
        this.enabled =  true ;
    }
    public String getName() {
        return this.name ;
    }
    public void rename(String newName) {
        this.name = newName ;
    }

    public FlowNode getFlowNode() {
        return this.flowNode ;
    }


}
